﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HalconDotNet;
using System.Windows.Forms;

namespace 基于灰度的裂纹长度测量系统
{
    class CrackDetect
    {
        //镜像裂纹参数刷新委托
        public delegate void ChangeDirection(bool fresh);
        public event ChangeDirection change;


        //原裂纹图
        HObject ho_Image;
        //模板图像
        HObject ho_ImagePart;
        //裂纹区域
        HObject crackImage;
        //滤波
        HObject filterImage;
        //sobel轮廓
        HObject ho_EdgeAmplitude, ho_ImageConverted;
        //叠加结果
        HObject addOrMutiImage;
        //裂纹骨架
        HObject ho_Skeleton;
        //根据斜率的最后骨架结果
        HObject ho_Region_Res;


        //模板创建结果
        HTuple hv_ModelID = null;
        //模板匹配结果中心点坐标，角度，置信度得分
        HTuple hv_Row, hv_Column, hv_Angle, hv_Score;
        //起点全局坐标
        HTuple hv_x = null, hv_y = null;
        //终点计算所需坐标
        HTuple x = null, y = null;


        // 裂纹图像picturebox1句柄
        HTuple MainWindowHandle;
        // 模板picturebox2句柄
        HTuple ModelWindowHandle;


        /// <summary>
        /// 读图
        /// </summary>
        /// <param name="path"></param>
        /// <param name="pictureBox"></param>
        public void ReadImage(string path, PictureBox pictureBox)
        {
            //传入句柄
            HTuple FatherWindow = pictureBox.Handle;
            //读图
            HOperatorSet.ReadImage(out ho_Image, path);
            //防止更改第一次句柄
            if (MainWindowHandle == null)
            {
                //打开句柄窗口
                HOperatorSet.OpenWindow(0, 0, pictureBox.Width, pictureBox.Height, FatherWindow, "visible", "", out MainWindowHandle);
            }
            //获取裂纹图像参数
            HOperatorSet.GetImagePointer1(ho_Image, out _, out _, out HTuple Width, out HTuple Height);
            //句柄窗口自适应图片大小
            HOperatorSet.SetPart(MainWindowHandle, 0, 0, Height - 1, Width - 1);
            clearPicturebox();
            //图像刷新，同步刷新指示镜像裂纹参数
            change(true);
        }

        /// <summary>
        /// 检查裂纹图像
        /// </summary>
        /// <returns></returns>
        public bool check_CrackImage()
        {
            if (ho_Image == null)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// 检查模板图像
        /// </summary>
        /// <returns></returns>
        public bool check_ModelImage()
        {
            if (ho_ImagePart == null)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// 清空窗口并显示图像
        /// </summary>
        /// <param name="windowHandle"></param>
        public void clearPicturebox()
        {
            HOperatorSet.ClearWindow(MainWindowHandle);
            HOperatorSet.DispObj(ho_Image, MainWindowHandle);
        }

        /// <summary>
        /// 镜像
        /// </summary>
        /// <param name="pictureBox"></param>
        public void MirroredImage(PictureBox pictureBox)
        {
            //镜像图像
            HOperatorSet.MirrorImage(ho_Image, out ho_Image, "column");
            //获取裂纹图像参数
            HOperatorSet.GetImagePointer1(ho_Image, out _, out _, out HTuple Width, out HTuple Height);
            //句柄窗口自适应图片大小
            HOperatorSet.SetPart(MainWindowHandle, 0, 0, Height - 1, Width - 1);
            clearPicturebox();
        }

        /// <summary>
        /// 创建模板
        /// </summary>
        /// <param name="b">true创建；false读取</param>
        /// <param name="pictureBox">显示模板picturebox</param>
        /// <param name="path">读取路径，不读取则传入空字符串即可</param>
        public void CreateModel(bool b, PictureBox pictureBox, string path)
        {
            //创建模板
            HObject ho_ROI_1, ho_CrackImage1;
            //传入句柄
            HTuple FatherWindow = pictureBox.Handle;
            //防止更改第一次句柄
            if (ModelWindowHandle == null)
            {
                //打开句柄窗口
                HOperatorSet.OpenWindow(0, 0, pictureBox.Width, pictureBox.Height, FatherWindow, "visible", "", out ModelWindowHandle);
            }
            if (b)
            {
                //ROI参数申明
                HTuple hv_Row1 = null, hv_Column1 = null, hv_Row2 = null, hv_Column2 = null;
                //红色画ROI
                HOperatorSet.SetColor(MainWindowHandle, "red");
                //画矩形
                HOperatorSet.DrawRectangle1(MainWindowHandle, out hv_Row1, out hv_Column1, out hv_Row2, out hv_Column2);
                //获得ROI
                HOperatorSet.GenRectangle1(out ho_ROI_1, hv_Row1, hv_Column1, hv_Row2, hv_Column2);
                //裁剪ROI
                HOperatorSet.ReduceDomain(ho_Image, ho_ROI_1, out ho_CrackImage1);
                //去除无关区域
                HOperatorSet.CropDomain(ho_CrackImage1, out ho_ImagePart);
            }
            else
            {
                //读图
                HOperatorSet.ReadImage(out HObject Image_read, path);
                //转灰度图
                HOperatorSet.Rgb1ToGray(Image_read, out ho_ImagePart);
            }
            //创建模板
            HOperatorSet.CreateNccModel(ho_ImagePart, "auto", 0, 0, "auto", "use_polarity", out hv_ModelID);
            //获得模板参数
            HOperatorSet.GetImagePointer1(ho_ImagePart, out _, out _, out HTuple Width, out HTuple Height);
            //自适应大小显示模板
            HOperatorSet.SetPart(ModelWindowHandle, 0, 0, Height - 1, Width - 1);
            //清空句柄窗口
            HOperatorSet.ClearWindow(ModelWindowHandle);
            //显示模板图像
            HOperatorSet.DispObj(ho_ImagePart, ModelWindowHandle);
        }

        /// <summary>
        /// 模板匹配
        /// </summary>
        public void ModelMatch()
        {
            try
            {
                HOperatorSet.FindNccModel(ho_Image, hv_ModelID, 0, 0, 0.5, 1, 0.5, "true", 0, out hv_Row, out hv_Column, out hv_Angle, out hv_Score);
            }
            catch (Exception ex)
            {

                throw new Exception("模板匹配失败，请重新选择或创建模板", ex);
            }
        }

        private void black_white(HObject ho_Image, out HObject ho_outImage, HTuple hv_window_size, HTuple hv_v_mid_pos, HTuple hv_thread)
        {
            HObject ho_Rectangle;
            HTuple hv_Width = null, hv_Height = null, hv_Rows = null;
            HTuple hv_Columns = null, hv_Grayval = null, hv_res = null;
            HTuple hv_i = null, hv_j = new HTuple(), hv_ans = new HTuple();
            HTuple hv_count = new HTuple(), hv_m = new HTuple(), hv_n = new HTuple();
            //创建输出图像
            HOperatorSet.CopyImage(ho_Image, out ho_outImage);

            //获取输入图像灰度数组
            HOperatorSet.GetImageSize(ho_Image, out hv_Width, out hv_Height);
            HOperatorSet.GenRectangle1(out ho_Rectangle, 0, 0, hv_Height - 1, hv_Width - 1);
            HOperatorSet.GetRegionPoints(ho_Rectangle, out hv_Rows, out hv_Columns);
            HOperatorSet.GetGrayval(ho_Image, hv_Rows, hv_Columns, out hv_Grayval);
            hv_res = HTuple.TupleGenConst(new HTuple(hv_Grayval.TupleLength()), 255);

            //    小于v_mid_pos 滑动窗口为       *
            //                                  * *
            //                                  * * *
            //
            //    大于等于v_mid_pos 滑动窗口为   * * *
            //                                  * *
            //                                  *
            //    滑动窗口灰度均值小于阈值为黑，大于阈值为白
            //    检测到一行第一个白点，则后面的均为白点
            HTuple end_val19 = hv_Height - 1;
            HTuple step_val19 = 1;
            for (hv_i = 0; hv_i.Continue(end_val19, step_val19); hv_i = hv_i.TupleAdd(step_val19))
            {
                HTuple end_val20 = hv_Width - 1;
                HTuple step_val20 = 1;
                for (hv_j = 0; hv_j.Continue(end_val20, step_val20); hv_j = hv_j.TupleAdd(step_val20))
                {
                    hv_ans = 0;
                    hv_count = 0;
                    if ((int)(new HTuple(hv_i.TupleLess(hv_v_mid_pos))) != 0)
                    {
                        HTuple end_val24 = hv_i + (hv_window_size - 1);
                        HTuple step_val24 = 1;
                        for (hv_m = hv_i; hv_m.Continue(end_val24, step_val24); hv_m = hv_m.TupleAdd(step_val24))
                        {
                            HTuple end_val25 = (((hv_m * hv_Width) + hv_j) + hv_window_size) - 1;
                            HTuple step_val25 = 1;
                            for (hv_n = (hv_m * hv_Width) + hv_j; hv_n.Continue(end_val25, step_val25); hv_n = hv_n.TupleAdd(step_val25))
                            {
                                if ((int)((new HTuple(((hv_m - hv_i)).TupleGreaterEqual(hv_n - ((hv_m * hv_Width) + hv_j)))).TupleAnd(
                                    new HTuple(hv_n.TupleLess((hv_m + 1) * hv_Width)))) != 0)
                                {
                                    hv_ans = hv_ans + (hv_Grayval.TupleSelect(hv_n));
                                    hv_count = hv_count + 1;
                                }
                            }
                        }
                    }
                    else
                    {
                        HTuple end_val33 = hv_i - (hv_window_size - 1);
                        HTuple step_val33 = -1;
                        for (hv_m = hv_i; hv_m.Continue(end_val33, step_val33); hv_m = hv_m.TupleAdd(step_val33))
                        {
                            HTuple end_val34 = (((hv_m * hv_Width) + hv_j) + hv_window_size) - 1;
                            HTuple step_val34 = 1;
                            for (hv_n = (hv_m * hv_Width) + hv_j; hv_n.Continue(end_val34, step_val34); hv_n = hv_n.TupleAdd(step_val34))
                            {
                                if ((int)((new HTuple(((hv_i - hv_m)).TupleGreaterEqual(hv_n - ((hv_m * hv_Width) + hv_j)))).TupleAnd(
                                    new HTuple(hv_n.TupleLess((hv_m + 1) * hv_Width)))) != 0)
                                {
                                    hv_ans = hv_ans + (hv_Grayval.TupleSelect(hv_n));
                                    hv_count = hv_count + 1;
                                }
                            }
                        }
                    }
                    if ((int)(new HTuple(((hv_ans / hv_count)).TupleLessEqual(hv_thread))) != 0)
                    {
                        if (hv_res == null)
                            hv_res = new HTuple();
                        hv_res[(hv_i * hv_Width) + hv_j] = 0;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            //改写输出图像
            HOperatorSet.SetGrayval(ho_outImage, hv_Rows, hv_Columns, hv_res);
            return;
        }

        private void get_mid_v(HObject ho_Image, HTuple hv_thread, HTuple hv_numbers, out HTuple hv_v_mid_pos)
        {
            HTuple hv_Width = null, hv_Height = null, hv_ans = null;
            HTuple hv_count = null, hv_i = null, hv_count2 = new HTuple();
            HTuple hv_j = new HTuple(), hv_Grayval = new HTuple();

            HOperatorSet.GetImageSize(ho_Image, out hv_Width, out hv_Height);

            //按行从左往右搜索，如果发现大于thread的点数多于numbers个，且还未搜索到从左往右5/6位置，break
            //v_mid_pos为符合条件的行标i加起来 / 找到的行数
            hv_ans = 0;
            hv_count = 0.0;
            HTuple end_val6 = hv_Height - 1;
            HTuple step_val6 = 1;
            for (hv_i = 0; hv_i.Continue(end_val6, step_val6); hv_i = hv_i.TupleAdd(step_val6))
            {
                hv_count2 = 0;
                HTuple end_val8 = hv_Width - 1;
                HTuple step_val8 = 1;
                for (hv_j = 0; hv_j.Continue(end_val8, step_val8); hv_j = hv_j.TupleAdd(step_val8))
                {
                    HOperatorSet.GetGrayval(ho_Image, hv_i, hv_j, out hv_Grayval);
                    if ((int)(new HTuple(hv_Grayval.TupleGreater(hv_thread))) != 0)
                    {
                        hv_count2 = hv_count2 + 1;
                    }
                    if ((int)((new HTuple(hv_count2.TupleGreater(hv_numbers))).TupleAnd(new HTuple(hv_j.TupleLess(
                        ((hv_Width - 1) / 6) * 5)))) != 0)
                    {
                        break;
                    }
                    if ((int)(new HTuple(hv_j.TupleEqual(hv_Width - 1))) != 0)
                    {
                        hv_ans = hv_ans + hv_i;
                        hv_count = hv_count + 1;
                    }
                }
            }
            hv_v_mid_pos = hv_ans / hv_count;
            return;
        }

        private bool gen_start(HTuple hv_RowHarris, HTuple hv_ColHarris, HTuple hv_mid_v, HTuple hv_thread_y, HTuple hv_thread_x, HTuple hv_Width, out HTuple hv_Row, out HTuple hv_Col)
        {
            HTuple hv_count = null, hv_i = null;
            hv_Row = 0;
            hv_Col = 0;
            hv_count = 0;
            for (hv_i = 0; (int)hv_i <= (int)((new HTuple(hv_RowHarris.TupleLength())) - 1); hv_i = (int)hv_i + 1)
            {
                if ((int)((new HTuple((((((hv_RowHarris.TupleSelect(hv_i)) - hv_mid_v)).TupleAbs()
                    )).TupleLessEqual(hv_thread_y))).TupleAnd(new HTuple((((hv_ColHarris.TupleSelect(
                    hv_i)) - (hv_thread_x * hv_Width))).TupleGreaterEqual(0)))) != 0)
                {
                    hv_Row = hv_Row + (hv_RowHarris.TupleSelect(hv_i));
                    hv_Col = hv_Col + (hv_ColHarris.TupleSelect(hv_i));
                    hv_count = hv_count + 1;
                }
            }
            if (hv_count == 0)
            {
                return false;
            }
            hv_Row = hv_Row / hv_count;
            hv_Col = hv_Col / hv_count;
            return true;
        }

        /// <summary>
        /// 起点检测
        /// </summary>
        public bool startDetect()
        {
            //模板匹配得到预制裂纹区域
            HObject ho_Rectangle, ho_CrackImage2, ho_ImagePart2;
            //预制裂纹区域预处理
            HObject ho_ImageMedian, ho_ImageEmphasize;
            //预制裂纹区域二值化图
            HObject ho_outImage;
            //模板图尺寸
            HTuple hv_Width = null, hv_Height = null;
            //预制裂纹区域二值化中点
            HTuple hv_v_mid_pos = null;
            //检测到的角点
            HTuple hv_RowHarris = null, hv_ColHarris = null;
            //预制裂纹区域ho_ImagePart2尺寸
            HTuple hv_Width2 = null, hv_Height2 = null;
            //起点局部坐标
            HTuple hv_RowStart = null, hv_ColStart = null;
            //获取预制裂纹区域
            HOperatorSet.GetImageSize(ho_ImagePart, out hv_Width, out hv_Height);
            HOperatorSet.GenRectangle2(out ho_Rectangle, hv_Row, hv_Column, hv_Angle, hv_Width / 2, hv_Height / 2);
            HOperatorSet.ReduceDomain(ho_Image, ho_Rectangle, out ho_CrackImage2);
            HOperatorSet.CropDomain(ho_CrackImage2, out ho_ImagePart2);
            //预处理
            HOperatorSet.MedianImage(ho_ImagePart2, out ho_ImageMedian, "square", 2, "mirrored");
            HOperatorSet.Emphasize(ho_ImageMedian, out ho_ImageEmphasize, 39, 39, 1);
            //寻找二值化中点
            try
            {
                get_mid_v(ho_ImageEmphasize, 20, 5, out hv_v_mid_pos);
            }
            catch (Exception ex)
            {

                throw new Exception("预制裂纹二值化中点寻找失败，尝试调大参数", ex);
            }
            //预制裂纹区域二值化
            try
            {
                black_white(ho_ImageEmphasize, out ho_outImage, 3, hv_v_mid_pos, 20);
            }
            catch (Exception ex)
            {

                throw new Exception("预制裂纹二值化失败，尝试调大参数", ex);
            }
            //角点检测
            HOperatorSet.PointsHarris(ho_outImage, 3.0, 2, 0.08, 10000, out hv_RowHarris, out hv_ColHarris);
            //显示起点
            HOperatorSet.GetImageSize(ho_ImagePart2, out hv_Width2, out hv_Height2);
            //如果默认参数检测不到角点，则通过循环改变参数重新检测
            int i = 4;
            for (; i < 10; i++)
            {
                if (!gen_start(hv_RowHarris, hv_ColHarris, hv_v_mid_pos, 20, 0.5, hv_Width2, out hv_RowStart, out hv_ColStart))
                {
                    //角点检测
                    HOperatorSet.PointsHarris(ho_outImage, i, 2, 0.08, 10000, out hv_RowHarris, out hv_ColHarris);
                }
                else
                {
                    break;
                }
            }
            //参数到10还不能检测，则模板选择的范围或其他参数有问题
            if (i == 10)
            {

                MessageBox.Show("获取起点失败，检测不到角点，二值化结果不佳，尝试重新选择模板或改变参数");
                return false;
            }
            //没问题计算全局起点
            hv_x = (hv_Row - ((hv_Height2 - 1) / 2)) + hv_RowStart;
            hv_y = (hv_Column - ((hv_Width2 - 1) / 2)) + hv_ColStart;
            //绿色画起点
            HOperatorSet.SetColor(MainWindowHandle, "green");
            HOperatorSet.DispCross(MainWindowHandle, hv_x, hv_y, 20, 45);
            return true;
        }

        /// <summary>
        /// 手动选取裂纹区域
        /// </summary>
        public void drawCrackRegion()
        {
            //手动选择裂纹区域矩形参数
            HTuple hv_Row11 = null, hv_Column11 = null, hv_Row22 = null, hv_Column22 = null;
            //手动选择裂纹区域
            HObject ho_ROI_2, ho_CrackImage3;
            HOperatorSet.DrawRectangle1(MainWindowHandle, out hv_Row11, out hv_Column11, out hv_Row22, out hv_Column22);
            HOperatorSet.GenRectangle1(out ho_ROI_2, hv_Row11, hv_Column11, hv_Row22, hv_Column22);
            HOperatorSet.ReduceDomain(ho_Image, ho_ROI_2, out ho_CrackImage3);
            HOperatorSet.CropDomain(ho_CrackImage3, out crackImage);
            x = hv_Row11;
            y = hv_Column11;
        }

        /// <summary>
        /// 自动识别裂纹区域
        /// </summary>
        /// <param name="size"></param>
        public void autoCrackRegion(int size, int superpose, float energy, float distance, float difference)
        {
            //原裂纹图尺寸
            HTuple hv_Width1 = null, hv_Height1 = null;
            //自动选取裂纹区域
            HObject ho_Rectangle1, ho_CrackImage4;
            HOperatorSet.GetImageSize(ho_Image, out hv_Width1, out hv_Height1);
            HOperatorSet.GenRectangle1(out ho_Rectangle1, hv_x - size, hv_y, hv_x + size, hv_Width1 - 1);
            HOperatorSet.ReduceDomain(ho_Image, ho_Rectangle1, out ho_CrackImage4);
            HOperatorSet.CropDomain(ho_CrackImage4, out crackImage);
            //裂纹骨架
            crackDTSkeleton(1, superpose, energy, distance, difference);
            //裂纹终点检测参数
            HTuple hv_Rows = null, hv_Columns = null;
            HTuple hv_Max = null, hv_indices = null;
            HObject ho_EndPoints, ho_JuncPoints;
            //初检测终点
            HOperatorSet.JunctionsSkeleton(ho_Region_Res, out ho_EndPoints, out ho_JuncPoints);
            HOperatorSet.GetRegionPoints(ho_EndPoints, out hv_Rows, out hv_Columns);
            HOperatorSet.TupleMax(hv_Columns, out hv_Max);
            HOperatorSet.TupleFind(hv_Columns, hv_Max, out hv_indices);
            HTuple endx = null, endy = null;
            endx = (hv_x - size) + (hv_Rows.TupleSelect(hv_indices));
            endy = hv_y + (hv_Columns.TupleSelect(hv_indices));
            HTuple res_xmin, res_xmax;
            //确定最终裂纹区域
            if (hv_x < endx)
            {
                res_xmin = hv_x;
                res_xmax = endx;
            }
            else
            {
                res_xmin = endx;
                res_xmax = hv_x;
            }
            HOperatorSet.GenRectangle1(out ho_Rectangle1, res_xmin - 10, hv_y, res_xmax + 10, endy + 20);
            HOperatorSet.ReduceDomain(ho_Image, ho_Rectangle1, out ho_CrackImage4);
            HOperatorSet.CropDomain(ho_CrackImage4, out crackImage);
            x = res_xmin - 10;
            y = hv_y;
        }

        /// <summary>
        /// 滤波选择
        /// </summary>
        /// <param name="num"></param>
        private void filterChoose(int filter)
        {
            if (filter == 0)
            {
                //自定义中值滤波
                median_mine(crackImage, out filterImage, 2);
            }
            else
            {
                //均值滤波
                HOperatorSet.MeanImage(crackImage, out filterImage, 5, 1);
            }
        }

        private void median_mine(HObject ho_Image, out HObject ho_Median_mine, HTuple hv_size)
        {
            HObject ho_Rectangle;
            HTuple hv_Width = null, hv_Height = null, hv_Rows = null;
            HTuple hv_Columns = null, hv_Grayval = null, hv_res = null;
            HTuple hv_i = null, hv_j = new HTuple(), hv_left = new HTuple();
            HTuple hv_right = new HTuple(), hv_Selected = new HTuple();
            HTuple hv_Median = new HTuple();

            //创建输出图像
            HOperatorSet.CopyImage(ho_Image, out ho_Median_mine);

            //获取输入图像灰度数组
            HOperatorSet.GetImageSize(ho_Image, out hv_Width, out hv_Height);
            HOperatorSet.GenRectangle1(out ho_Rectangle, 0, 0, hv_Height - 1, hv_Width - 1);
            HOperatorSet.GetRegionPoints(ho_Rectangle, out hv_Rows, out hv_Columns);
            HOperatorSet.GetGrayval(ho_Image, hv_Rows, hv_Columns, out hv_Grayval);
            hv_res = hv_Grayval.Clone();

            //处理中间区域
            HTuple end_val11 = hv_Height - 1;
            HTuple step_val11 = 1;
            for (hv_i = 0; hv_i.Continue(end_val11, step_val11); hv_i = hv_i.TupleAdd(step_val11))
            {
                HTuple end_val12 = (hv_Width - 1) - hv_size;
                HTuple step_val12 = 1;
                for (hv_j = hv_size; hv_j.Continue(end_val12, step_val12); hv_j = hv_j.TupleAdd(step_val12))
                {
                    hv_left = ((hv_i * hv_Width) + hv_j) - hv_size;
                    hv_right = ((hv_i * hv_Width) + hv_j) + hv_size;
                    HOperatorSet.TupleSelectRange(hv_Grayval, hv_left, hv_right, out hv_Selected);
                    HOperatorSet.TupleMedian(hv_Selected, out hv_Median);
                    if (hv_res == null)
                        hv_res = new HTuple();
                    hv_res[(hv_i * hv_Width) + hv_j] = hv_Median;
                }
            }

            //处理左边缘
            HTuple end_val22 = hv_Height - 1;
            HTuple step_val22 = 1;
            for (hv_i = 0; hv_i.Continue(end_val22, step_val22); hv_i = hv_i.TupleAdd(step_val22))
            {
                HTuple end_val23 = hv_size - 1;
                HTuple step_val23 = 1;
                for (hv_j = 0; hv_j.Continue(end_val23, step_val23); hv_j = hv_j.TupleAdd(step_val23))
                {
                    hv_left = hv_i * hv_Width;
                    hv_right = ((hv_i * hv_Width) + hv_j) + hv_size;
                    HOperatorSet.TupleSelectRange(hv_Grayval, hv_left, hv_right, out hv_Selected);
                    HOperatorSet.TupleMedian(hv_Selected, out hv_Median);
                    if (hv_res == null)
                        hv_res = new HTuple();
                    hv_res[(hv_i * hv_Width) + hv_j] = hv_Median;
                }
            }

            //处理右边缘
            HTuple end_val33 = hv_Height - 1;
            HTuple step_val33 = 1;
            for (hv_i = 0; hv_i.Continue(end_val33, step_val33); hv_i = hv_i.TupleAdd(step_val33))
            {
                HTuple end_val34 = hv_Width - 1;
                HTuple step_val34 = 1;
                for (hv_j = hv_Width - hv_size; hv_j.Continue(end_val34, step_val34); hv_j = hv_j.TupleAdd(step_val34))
                {
                    hv_left = ((hv_i * hv_Width) + hv_j) - hv_size;
                    hv_right = (hv_i * hv_Width) + hv_j;
                    HOperatorSet.TupleSelectRange(hv_Grayval, hv_left, hv_right, out hv_Selected);
                    HOperatorSet.TupleMedian(hv_Selected, out hv_Median);
                    if (hv_res == null)
                        hv_res = new HTuple();
                    hv_res[(hv_i * hv_Width) + hv_j] = hv_Median;
                }
            }

            //改写输出图像
            HOperatorSet.SetGrayval(ho_Median_mine, hv_Rows, hv_Columns, hv_res);
            return;
        }

        /// <summary>
        /// 叠加选择
        /// </summary>
        /// <param name="filter"></param>
        /// <param name="choose"></param>
        private void addOrMuti(int superpose)
        {
            if (superpose == 0)
            {
                muti();
            }
            else
            {
                add();
            }
        }


        /// <summary>
        /// 加法
        /// </summary>
        private void add()
        {
            HObject ho_ImageEmphasize2, ho_ImageInvert1, ho_ImageResult, ho_ImageEmphasize1;
            HOperatorSet.Emphasize(ho_ImageConverted, out ho_ImageEmphasize2, 15, 15, 1);
            HOperatorSet.InvertImage(ho_ImageEmphasize2, out ho_ImageInvert1);
            HOperatorSet.AddImage(filterImage, ho_ImageInvert1, out ho_ImageResult, 0.5, 0);
            {
                HObject ExpTmpOutVar_0;
                HOperatorSet.AddImage(ho_ImageResult, ho_ImageInvert1, out ExpTmpOutVar_0, 0.5, 0);
                ho_ImageResult = ExpTmpOutVar_0;
            }
            HOperatorSet.Emphasize(ho_ImageResult, out ho_ImageEmphasize1, 15, 15, 1);
            HOperatorSet.EquHistoImage(ho_ImageEmphasize1, out addOrMutiImage);
        }

        /// <summary>
        /// 乘法
        /// </summary>
        private void muti()
        {
            HObject ho_ImageEmphasize2, ho_ImageResult1;
            HOperatorSet.Emphasize(ho_ImageConverted, out ho_ImageEmphasize2, 15, 15, 1);
            HOperatorSet.MultImage(filterImage, ho_ImageEmphasize2, out ho_ImageResult1, 0.5, 0);
            HOperatorSet.InvertImage(ho_ImageResult1, out addOrMutiImage);
        }

        public void select_skeleton(HObject ho_Skeleton, out HObject ho_Region_Res, HTuple hv_distance, HTuple hv_angleSub)
        {
            // Stack for temporary objects 
            HObject[] OTemp = new HObject[20];
            // Local iconic variables 
            HObject ho_SelectedRegions, ho_EndPoints, ho_JuncPoints;
            HObject ho_ObjectSelected = null, ho_ObjectSelected1 = null;
            HObject ho_TempSelected2 = null, ho_ObjectSelected2 = null;
            // Local control variables 
            HTuple hv_Rows = null, hv_Columns = null, hv_Max = null;
            HTuple hv_Min = null, hv_Indices1 = null, hv_Indices2 = null;
            HTuple hv_Mean1 = null, hv_Mean2 = null, hv_MainRowBegin = null;
            HTuple hv_MainColBegin = null, hv_MainRowEnd = null, hv_MainColEnd = null;
            HTuple hv_MainPhi = null, hv_Number = null, hv_i = null;
            HTuple hv_count = new HTuple(), hv_RegionIndex1 = new HTuple();
            HTuple hv_RegionIndex2 = new HTuple(), hv_RegionIndex11 = new HTuple();
            HTuple hv_RegionIndex21 = new HTuple(), hv_Number1 = new HTuple();
            HTuple hv_j = new HTuple(), hv_Phi = new HTuple();

            //提取主裂纹起点和终点，以及斜率
            HOperatorSet.SelectShapeStd(ho_Skeleton, out ho_SelectedRegions, "max_area", 70);
            HOperatorSet.JunctionsSkeleton(ho_SelectedRegions, out ho_EndPoints, out ho_JuncPoints);
            HOperatorSet.GetRegionPoints(ho_EndPoints, out hv_Rows, out hv_Columns);
            HOperatorSet.TupleMax(hv_Columns, out hv_Max);
            HOperatorSet.TupleMin(hv_Columns, out hv_Min);
            HOperatorSet.TupleFind(hv_Columns, hv_Max, out hv_Indices1);
            HOperatorSet.TupleFind(hv_Columns, hv_Min, out hv_Indices2);
            HOperatorSet.TupleMean(hv_Rows.TupleSelect(hv_Indices1), out hv_Mean1);
            HOperatorSet.TupleMean(hv_Rows.TupleSelect(hv_Indices2), out hv_Mean2);
            hv_MainRowBegin = hv_Mean2.Clone();
            hv_MainColBegin = hv_Min.Clone();
            hv_MainRowEnd = hv_Mean1.Clone();
            hv_MainColEnd = hv_Max.Clone();
            HOperatorSet.LineOrientation(hv_MainRowBegin, hv_MainColBegin, hv_MainRowEnd, hv_MainColEnd, out hv_MainPhi);
            HOperatorSet.SelectShapeStd(ho_Skeleton, out ho_Region_Res, "max_area", 70);

            //返回结果Region_Res，当前搜索到的裂纹SelectedRegions
            HOperatorSet.CountObj(ho_Skeleton, out hv_Number);
            HTuple end_val20 = hv_Number;
            HTuple step_val20 = 1;
            for (hv_i = 1; hv_i.Continue(end_val20, step_val20); hv_i = hv_i.TupleAdd(step_val20))
            {
                hv_count = 0;
                HOperatorSet.FindNeighbors(ho_SelectedRegions, ho_Skeleton, hv_distance,
                    out hv_RegionIndex1, out hv_RegionIndex2);
                HOperatorSet.SelectObj(ho_Skeleton, out ho_ObjectSelected, hv_RegionIndex2);
                HOperatorSet.SelectRegionSpatial(ho_SelectedRegions, ho_ObjectSelected, "right",
                    out hv_RegionIndex11, out hv_RegionIndex21);
                HOperatorSet.SelectObj(ho_ObjectSelected, out ho_ObjectSelected1, hv_RegionIndex21);

                HOperatorSet.CountObj(ho_ObjectSelected1, out hv_Number1);
                HOperatorSet.JunctionsSkeleton(ho_ObjectSelected1, out ho_EndPoints, out ho_JuncPoints);
                HTuple end_val29 = hv_Number1;
                HTuple step_val29 = 1;
                for (hv_j = 1; hv_j.Continue(end_val29, step_val29); hv_j = hv_j.TupleAdd(step_val29))
                {
                    HOperatorSet.SelectObj(ho_EndPoints, out ho_TempSelected2, hv_j);
                    HOperatorSet.GetRegionPoints(ho_TempSelected2, out hv_Rows, out hv_Columns);
                    HOperatorSet.TupleMin(hv_Columns, out hv_Min);
                    if ((int)(new HTuple(hv_Min.TupleLess(hv_MainColEnd))) != 0)
                    {
                        continue;
                    }
                    HOperatorSet.TupleMax(hv_Columns, out hv_Max);
                    HOperatorSet.TupleFind(hv_Columns, hv_Max, out hv_Indices1);
                    HOperatorSet.TupleMean(hv_Rows.TupleSelect(hv_Indices1), out hv_Mean1);
                    HOperatorSet.LineOrientation(hv_MainRowBegin, hv_MainColBegin, hv_Mean1,
                        hv_Max, out hv_Phi);
                    if ((int)(new HTuple(((((hv_Phi - hv_MainPhi)).TupleAbs())).TupleLessEqual(
                        hv_angleSub))) != 0)
                    {
                        HOperatorSet.SelectObj(ho_ObjectSelected1, out ho_ObjectSelected2, hv_j);
                        {
                            HObject ExpTmpOutVar_0;
                            HOperatorSet.Union2(ho_Region_Res, ho_ObjectSelected2, out ExpTmpOutVar_0);
                            ho_Region_Res = ExpTmpOutVar_0;
                        }
                        HOperatorSet.SelectObj(ho_ObjectSelected1, out ho_SelectedRegions, hv_j);
                        hv_count = 1;
                        break;
                    }
                }
                if ((int)(new HTuple(hv_count.TupleEqual(0))) != 0)
                {
                    break;
                }
            }
            return;
        }

        public void select_mask_obj(HObject ho_Objects, out HObject ho_SelectedObjects, HTuple hv_Mask)
        {
            HTuple hv_Number = null, hv_AllNumbers = null;
            HTuple hv_Indices = null;
            //Check number of elements
            HOperatorSet.CountObj(ho_Objects, out hv_Number);
            if ((int)(new HTuple(hv_Number.TupleNotEqual(new HTuple(hv_Mask.TupleLength()
                )))) != 0)
            {
                throw new HalconException("Number of elements in Objects and Mask do not match.");
            }
            //Check type of mask elements
            hv_AllNumbers = new HTuple((((((hv_Mask.TupleIsRealElem())).TupleSum()) + (((hv_Mask.TupleIsIntElem()
                )).TupleSum()))).TupleEqual(new HTuple(hv_Mask.TupleLength())));
            if ((int)((new HTuple(hv_AllNumbers.TupleNot())).TupleAnd(new HTuple(hv_Mask.TupleNotEqual(
                new HTuple())))) != 0)
            {
                throw new HalconException("Invalid type: Elements of Mask must be integer or real numbers.");
            }
            //Use select_mask for tuples to generate a list of object indices.
            hv_Indices = (HTuple.TupleGenSequence(1, new HTuple(hv_Mask.TupleLength()), 1)).TupleSelectMask(
                hv_Mask);
            HOperatorSet.SelectObj(ho_Objects, out ho_SelectedObjects, hv_Indices);
            return;
        }

        /// <summary>
        /// 裂纹检测
        /// </summary>
        public void crackDTSkeleton(int filter, int superpose, float energy, float distance, float difference)
        {
            HObject ho_Region1;
            HObject ho_RegionOpening, ho_ConnectedRegions, ho_SelectedRegions;
            HObject ho_Defects;
            HTuple hv_UsedThreshold = null;
            HTuple hv_Energy, hv_Correlation, hv_Homogeneity, hv_Contrast;
            HTuple hv_Energy1, hv_Correlation1, hv_Homogeneity1, hv_Contrast1;
            HTuple hv_Mask;

            //滤波选择
            filterChoose(filter);
            //裂纹识别流程
            //sobel算子提取轮廓
            HOperatorSet.SobelAmp(filterImage, out ho_EdgeAmplitude, "y_binomial", 5);
            //转换图像格式
            HOperatorSet.ConvertImageType(ho_EdgeAmplitude, out ho_ImageConverted, "byte");
            //叠加选择
            addOrMuti(superpose);
            //自适应阈值分割
            HOperatorSet.BinaryThreshold(addOrMutiImage, out ho_Region1, "max_separability", "dark", out hv_UsedThreshold);
            //开操作
            HOperatorSet.OpeningRectangle1(ho_Region1, out ho_RegionOpening, 15, 1);
            //连通域
            HOperatorSet.Connection(ho_RegionOpening, out ho_ConnectedRegions);
            //计算连通域的灰度共生矩阵参数
            HOperatorSet.CoocFeatureImage(ho_ConnectedRegions, filterImage, 6, 0, out hv_Energy, out hv_Correlation, out hv_Homogeneity, out hv_Contrast);
            //最大主裂纹选择
            HOperatorSet.SelectShapeStd(ho_ConnectedRegions, out ho_SelectedRegions, "max_area", 70);
            //计算最大主裂纹的灰度共生矩阵参数
            HOperatorSet.CoocFeatureImage(ho_SelectedRegions, filterImage, 6, 0, out hv_Energy1, out hv_Correlation1, out hv_Homogeneity1, out hv_Contrast1);
            //根据最大主裂纹能量参数筛选连通域裂纹 hv_Energy <= hv_Energy1 + 0.01
            hv_Mask = hv_Energy.TupleLessEqualElem(hv_Energy1 + energy);
            select_mask_obj(ho_ConnectedRegions, out ho_Defects, hv_Mask);
            //提取裂纹骨架
            HOperatorSet.Skeleton(ho_Defects, out ho_Skeleton);
            select_skeleton(ho_Skeleton, out ho_Region_Res, distance, difference);
        }


        /// <summary>
        /// 裂纹和终点显示
        /// </summary>
        public double crack_EndShow()
        {
            HTuple hv_Rows1 = null;
            HTuple hv_Columns1 = null, hv_Rows = null, hv_Columns = null;
            HTuple hv_Max = null, hv_indices = null;
            HObject ho_Region, ho_EndPoints, ho_JuncPoints;
            //显示裂纹
            HOperatorSet.GetRegionPoints(ho_Region_Res, out hv_Rows1, out hv_Columns1);
            hv_Rows1 = hv_Rows1 + x;
            hv_Columns1 = hv_Columns1 + y;
            HOperatorSet.GenRegionPoints(out ho_Region, hv_Rows1, hv_Columns1);
            //红色画裂纹
            HOperatorSet.SetColor(MainWindowHandle, "red");
            HOperatorSet.DispRegion(ho_Region, MainWindowHandle);
            //显示终点
            HOperatorSet.JunctionsSkeleton(ho_Region_Res, out ho_EndPoints, out ho_JuncPoints);
            HOperatorSet.GetRegionPoints(ho_EndPoints, out hv_Rows, out hv_Columns);
            HOperatorSet.TupleMax(hv_Columns, out hv_Max);
            HOperatorSet.TupleFind(hv_Columns, hv_Max, out hv_indices);
            //绿色画终点
            HOperatorSet.SetColor(MainWindowHandle, "green");
            HOperatorSet.DispCross(MainWindowHandle, x + (hv_Rows.TupleSelect(
                hv_indices)), y + (hv_Columns.TupleSelect(hv_indices)), 20, 45);
            double length = y + (hv_Columns.TupleSelect(hv_indices)) - hv_y;
            return length;
        }
    }
}
